package com.yuushya.collision;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.yuushya.collision.data.BlockState;
import com.yuushya.collision.data.CollisionItem;
import com.yuushya.collision.data.Model;
import com.yuushya.collision.utils.ExpandModel;
import com.yuushya.collision.utils.OptimizeModel;
import com.yuushya.collision.utils.RotateModel;
import com.yuushya.datagen.ConfigReader;
import com.yuushya.datagen.utils.ResourceLocation;
import com.yuushya.ui.Mode;
import com.yuushya.ui.YuushyaLog;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.yuushya.datagen.ConfigReader.StaticBlockALL;
import static com.yuushya.datagen.ConfigReader.TemplateBrother;
import static com.yuushya.datagen.utils.Utils.MOD_ID;
import static com.yuushya.utils.GsonTools.NormalGSON;

public class CollisionFileCreator {

    private final Path _basePath;
    private Path _resPath;
    private final String _nameSpace ;
    private static final CollisionItem.Model.Element CUBE = new CollisionItem.Model.Element(0.0,0.0,0.0,16.0,16.0,16.0);
    private final Map<String,CollisionItem> collisionData = new HashMap<>();
    public CollisionFileCreator(String nameSpace,Path basePath){
        this._nameSpace = nameSpace;
        this._resPath =  Path.of("../config/com.yuushya/"+nameSpace+"/");
        this._basePath=basePath;
    }
    public CollisionFileCreator(Path basePath,Path resPath){
        this._nameSpace = MOD_ID;
        this._basePath = basePath;
        this._resPath = resPath;
    }
    public enum CollisionType{
        NONE,BLOCK,BOUND,DETAIL,DETAIL30,FACE;
        public static CollisionType from(String type){
            if(type==null) return NONE;
            return switch (type){
                case "null","none","false",""->NONE;
                case "block"->BLOCK;
                case "bound"->BOUND;
                case "detail"->DETAIL;
                case "detail30"->DETAIL30;
                case "face"->FACE;
                default -> BLOCK;
            };
        }
        public static boolean isNone(String type){
            return switch (type){
                case "null","none","false",""->true;
                default -> false;
            };
        }
    }

    record ModelTriple(ResourceLocation resourceLocation,Integer x,Integer y){}
    static Map<ResourceLocation,Model> modelCache = new HashMap<>();
    static Map<ModelTriple,List<CollisionItem.Model.Element>> collisionCache = new HashMap<>();
    private void readBlockStateAndModel(){
        if(Mode.proposalCollision!=null) Mode.proposalCollision.add("COLLISION PROPOSE:\t{namespaceId}\t{TYPE}\t{model}\t{totalCube}\t{face}\t{bigCube}\t{smallCube}");
        Path path = _basePath.resolve("./assets/"+ _nameSpace +"/blockstates/");//read the all blockstates under the namespace
        ModelReader modelReader = new ModelReader(this._basePath);
        if(path.toFile().listFiles()!=null){
            for(var blockstateFile:path.toFile().listFiles() ) {

                ResourceLocation namespaceId = new ResourceLocation(_nameSpace,blockstateFile.getName().replace(".json",""));
                String namespaceIdString = namespaceId.toString();
                //这里没做namespaceId，默认是yuushya
                if(TemplateBrother.containsKey(namespaceId.getPath())&&!TemplateBrother.get(namespaceId.getPath()).equals(namespaceId.getPath())){
                    String parent = TemplateBrother.get(namespaceId.getPath());
                    String parentNamespaceId= new ResourceLocation(_nameSpace,parent).toString();
                    if(!collisionData.containsKey(parentNamespaceId)){
                        collisionData.put(parentNamespaceId,new CollisionItem());
                    }
                    if(collisionData.get(parentNamespaceId).children == null){collisionData.get(parentNamespaceId).children = new ArrayList<>(); }
                    collisionData.get(parentNamespaceId).children.add(namespaceIdString);
                }
                else if(StaticBlockALL.containsKey(namespaceId.getPath())){
                    String collisionType = StaticBlockALL.get(namespaceId.getPath()).autoGenerated.collision;
                    if (collisionType!=null && !CollisionType.isNone(collisionType)) {
                        CollisionItem item = new CollisionItem();
                        item.blockstates = new ArrayList<>();
                        try (BufferedReader reader=new BufferedReader(new FileReader(blockstateFile))){
                            JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject();
                            if(obj.has("variants")) {//先不做multipart了
                                for(var pair:obj.getAsJsonObject("variants").entrySet()){
                                    CollisionItem.Model itemBlockstate = new CollisionItem.Model();
                                    itemBlockstate.variant = pair.getKey();//.isEmpty()? "empty": pair.getKey().replace("=","#");
                                    JsonElement value = pair.getValue();
                                    if(value.isJsonArray()){ value = value.getAsJsonArray().get(0); }
                                    BlockState.Variant variant = NormalGSON.fromJson(value, BlockState.Variant.class);
                                    ResourceLocation modelLocation = ResourceLocation.parse(variant.model);
                                    Model model;
                                    if(modelCache.containsKey(modelLocation)) model = modelCache.get(modelLocation);
                                    else model = modelReader.read( modelLocation);

                                    ModelTriple key = new ModelTriple(modelLocation, variant.x, variant.y);
                                    if(collisionCache.containsKey(key)) {
                                        itemBlockstate.collision = collisionCache.get(key);
                                        item.blockstates.add(itemBlockstate);
                                    }
                                    else if(model!=null){
                                        if(model.elements==null){
                                            itemBlockstate.collision = new ArrayList<>();
                                            itemBlockstate.collision.add(CUBE);
                                            if(Mode.proposalCollision!=null) Mode.proposalCollision.add( MessageFormat.format( "COLLISION PROPOSE:\t{0}\tblock\t{1}\t0\t0\t0\t0",namespaceId.getPath(),variant.model));
                                        }
                                        else {
                                            itemBlockstate.collision = RotateModel.rotate(model, variant.x, variant.y);
                                            if(Mode.proposalCollision!=null) OptimizeModel.propose(namespaceId.getPath(),variant.model,itemBlockstate.collision);
                                            switch (CollisionType.from(collisionType)) {
                                                case FACE -> {
                                                    itemBlockstate.collision = ExpandModel.expandFace(itemBlockstate.collision,1);
                                                    itemBlockstate.collision = OptimizeModel.optimize(itemBlockstate.collision, 15);
                                                }
                                                case DETAIL30 -> {
                                                    itemBlockstate.collision = OptimizeModel.optimize(itemBlockstate.collision, 30);
                                                }
                                                case DETAIL -> {
                                                    itemBlockstate.collision = OptimizeModel.optimize(itemBlockstate.collision, 15);
                                                }
                                                case BOUND -> {
                                                    itemBlockstate.collision = OptimizeModel.optimize(itemBlockstate.collision, 15);
                                                    itemBlockstate.collision = List.of(OptimizeModel.combine(itemBlockstate.collision));
                                                }
                                                default -> {
                                                    itemBlockstate.collision = new ArrayList<>();
                                                    itemBlockstate.collision.add(CUBE);
                                                }
                                            }
                                        }
                                        item.blockstates.add(itemBlockstate);
                                        collisionCache.put(key,itemBlockstate.collision);
                                    }
                                    else{
                                        System.out.println("Error on read model: "+variant.model);
                                        YuushyaLog.warn("Error on read model: "+variant.model);
                                    }
                                }
                            }
                        }catch (IOException e){e.printStackTrace();YuushyaLog.error(e);}
                        if(collisionData.containsKey(namespaceIdString)){
                            collisionData.get(namespaceIdString).blockstates = item.blockstates;}
                        else{
                            collisionData.put(namespaceIdString,item);
                        }
                    }
                }

            }
        }
    }

    private void writeCollision(){
        Path path = this._resPath.resolve("./data/"+this._nameSpace+"/collision/");
        path.toFile().mkdirs();
        for(String namespaceIdString:collisionData.keySet()){
            ResourceLocation namespaceId = ResourceLocation.parse(namespaceIdString);
            Path itemPath = path.resolve(namespaceId.getPath()+".json");
            try(BufferedWriter writer=new BufferedWriter( new FileWriter(itemPath.toFile(), StandardCharsets.UTF_8))){
                String json=NormalGSON.toJson(collisionData.get(namespaceIdString));
                json= json.replaceAll("((?<=[,\\[])\n\s*(?=[-0-9]|\"to\"))|((?<=[0-9])\n\s*(?=]))|((?<=\\{)\n\s*(?=\"from\"))"," ").replaceAll("((?<=([0-9] ]))\n\s*(?=}))"," ");
                writer.write(json);
            }catch (IOException e){e.printStackTrace();YuushyaLog.error(e);}
        }
    }
    public void create(){
        readBlockStateAndModel();
        writeCollision();
    }

    public void createJson(){
        readBlockStateAndModel();
        this._resPath =this._basePath;
        writeCollision();
    }
}
